package com.liunian.channelhandler.handler;

import com.liunian.compress.Compressor;
import com.liunian.compress.CompressorFactory;
import com.liunian.serialize.Serializer;
import com.liunian.serialize.SerializerFactory;
import com.liunian.transport.message.MessageFormatConstant;
import com.liunian.transport.message.RequestPayload;
import com.liunian.transport.message.WrpcRequest;
import com.liunian.wrpcenum.RequestType;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import lombok.extern.slf4j.Slf4j;

import java.util.Random;

/**
 * 基于长度字段的帧解码器
 */
@Slf4j
public class WrpcRequestDecoder extends LengthFieldBasedFrameDecoder {
    public WrpcRequestDecoder() {
        super(
                // 找到当前报文的总长度，截取报文，截取出来的报文我们可以去解析。
                // 最大帧长度，超过这个 maxFrameLength 值会直接丢弃。
                MessageFormatConstant.MAX_FRAME_LENGTH,
                // 长度字段的偏移量
                MessageFormatConstant.MAGIC.length + MessageFormatConstant.VERSION_LENGTH + MessageFormatConstant.HEADER_FIELD_LENGTH,
                // 长度的字段的长度
                MessageFormatConstant.FULL_FIELD_LENGTH,
                // todo:负载的适配长度
                -(MessageFormatConstant.MAGIC.length + MessageFormatConstant.VERSION_LENGTH + MessageFormatConstant.HEADER_FIELD_LENGTH + MessageFormatConstant.FULL_FIELD_LENGTH),
                0);
    }

    @Override
    protected Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception {

        Thread.sleep(new Random().nextInt(50));

        Object decode = super.decode(ctx, in);
        if (decode instanceof ByteBuf) {
            ByteBuf byteBuf = (ByteBuf) decode;
            return decodeFrame(byteBuf);
        }
        return null;
    }

    private Object decodeFrame(ByteBuf byteBuf) {
        // 1、解析魔术值
        byte[] magic = new byte[MessageFormatConstant.MAGIC.length];
        byteBuf.readBytes(magic);
        // 检测魔术值是否匹配
        for (int i = 0; i < magic.length; i++) {
            if (magic[i] != MessageFormatConstant.MAGIC[i]) {
                throw new RuntimeException("获得的请求不合法。");
            }
        }

        // 2、解析版本号
        byte version = byteBuf.readByte();
        if (version > MessageFormatConstant.VERSION) {
            throw new RuntimeException("获得的请求版本不被支持。");
        }

        // 3、解析头部长度
        short headLength = byteBuf.readShort();

        // 4、解析总长度
        int fullLength = byteBuf.readInt();

        // 5、解析请求类型
        byte requestType = byteBuf.readByte();

        // 6、解析序列化类型
        byte serializeType = byteBuf.readByte();

        // 7、解析压缩类型
        byte compressType = byteBuf.readByte();

        // 8、解析请求的id
        long requestId = byteBuf.readLong();

        // 9、解析请求的时间戳
        long timeStamp = byteBuf.readLong();

        // 我们需要封装一个wrpcRequest
        WrpcRequest wrpcRequest = new WrpcRequest();
        wrpcRequest.setRequestType(requestType);
        wrpcRequest.setCompressType(compressType);
        wrpcRequest.setSerializeType(serializeType);
        wrpcRequest.setRequestId(requestId);
        wrpcRequest.setTimeStamp(timeStamp);
        // 如果是心跳请求没有负载，此处可以判断并直接返回。
        if (requestType == RequestType.HEART_BEAT.getId()) {
            return wrpcRequest;
        }

        int payloadLength = fullLength - headLength;
        byte[] payload = new byte[payloadLength];
        byteBuf.readBytes(payload);

        // 有了字节数组之后就可以解压缩和反序列化了

        if (payload != null && payload.length != 0) {
            // 解压缩，
            Compressor compressor = CompressorFactory.getCompressor(compressType).getImpl();
            payload = compressor.deCompress(payload);

            // 反序列化。
            // 1 --- jdk
            Serializer serializer = SerializerFactory.getSerializer(serializeType).getImpl();
            RequestPayload requestPayload = serializer.deserialize(payload, RequestPayload.class);
            wrpcRequest.setRequestPayload(requestPayload);
        }
        if (log.isDebugEnabled()) {
            log.debug("请求[{}]已经在服务端完成解码工作。", wrpcRequest.getRequestId());
        }

        return wrpcRequest;
    }
}
